﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
using CustomControls;

namespace Map
{
    public class MarkerDescriptor
    {
        public System.IO.Stream BgImageStream { get; set; }
        public Brush TextBrush { get; set; }
        public Font Font { get; set; }
        public string Text { get; set; }
        public Bitmap Bitmap { get; set; }
        public Point Offset { get; set; }
        public Point ImageOffset { get; set; }
    }

    public class MapPanel : Panel
    {
        public enum ButtonStateEnum { Show, Hide };
        public Map.MapControl Map;

        XButton buttonZoomIn, buttonZoomOut, buttonZoomArea, buttonShowHide;
        ButtonStateEnum ButtonState {get; set;}

        public MapPanel(ButtonStateEnum buttonState)
        {
            ButtonState = buttonState;
            Map = new MapControl();
            Map.Dock = DockStyle.Fill;
            this.Controls.Add(Map);

            Map.State = MapControl.MapState.Normal;

            buttonZoomIn = new XButton(new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.zoom_in.png")), new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.zoom_in_pressed.png")));
            buttonZoomIn.Click += new EventHandler(buttonZoomIn_Click);
            this.Controls.Add(buttonZoomIn);

            buttonZoomOut = new XButton(new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.zoom_out.png")), new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.zoom_out_pressed.png")));
            buttonZoomOut.Click += new EventHandler(buttonZoomOut_Click);
            this.Controls.Add(buttonZoomOut);

            buttonZoomArea = new XButton(new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.zoom_area.png")), new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.zoom_area_pressed.png")));
            buttonZoomArea.Click += new EventHandler(buttonZoomArea_Click);
            this.Controls.Add(buttonZoomArea);

            buttonShowHide = new XButton(new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.showhide.png")), new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.showhide_pressed.png")));
            buttonShowHide.Click += new EventHandler(buttonShowHide_Click);
            this.Controls.Add(buttonShowHide);
            SortButtons();

            Map.SendToBack();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            SortButtons();
        }

        bool SortButtons()
        {
            buttonZoomIn.Location = new Point(5, 5);
            buttonShowHide.Location = new Point(ClientSize.Width - buttonShowHide.Width - 5, ClientSize.Height - buttonShowHide.Height - 5);
            if ((double)ClientSize.Width / (double)ClientSize.Height < 1)
            {
                buttonZoomOut.Location = new Point(10 + buttonZoomIn.Width, 5);
                buttonZoomArea.Location = new Point(15 + buttonZoomOut.Width + buttonZoomIn.Width, 5);
                return true;
            }
            else
            {
                buttonZoomOut.Location = new Point(5, 10 + buttonZoomIn.Height);
                buttonZoomArea.Location = new Point(5, 15 + buttonZoomOut.Height + buttonZoomIn.Height);
                return false;
            }
        }

        void buttonZoomOut_Click(object sender, EventArgs e)
        {
            Map.ZoomOut();
        }

        void buttonZoomIn_Click(object sender, EventArgs e)
        {
            Map.ZoomIn();
        }

        void buttonZoomArea_Click(object sender, EventArgs e)
        {
            if (Map.State == MapControl.MapState.Normal)
            {
                Map.State = MapControl.MapState.AreaZoom;
                Map.Highlight();
            }
            else if(Map.State == MapControl.MapState.AreaZoom)
            {
                Map.State = MapControl.MapState.Normal;
                Map.RefreshBitmap();
            }
        }

        void buttonShowHide_Click(object sender, EventArgs e)
        {
            if (ButtonState == ButtonStateEnum.Show)
            {
                buttonZoomArea.Visible = false;
                buttonZoomIn.Visible = false;
                buttonZoomOut.Visible = false;
                ButtonState = ButtonStateEnum.Hide;
            }
            else if (ButtonState == ButtonStateEnum.Hide)
            {
                buttonZoomArea.Visible = true;
                buttonZoomIn.Visible = true;
                buttonZoomOut.Visible = true;
                ButtonState = ButtonStateEnum.Show;
            }
        }
    }

    public class MapControl : PictureBox
    {
        public enum MapState { Normal, AreaZoom };
        public MapState State { get; set; }

        double mapRatio;
        Point lastPos = Point.Empty;
        Bitmap bitmap, bitmapBuffer;
        Renderer renderer = new Renderer();
        GoogleClient session = new GoogleClient();

        Pen zoomRectanglePen = new Pen(Color.Black);

        static readonly object sync = new object();

        public delegate void RefreshBitmapDelegate();
        public RefreshBitmapDelegate refreshBitmapDelegate;

        public MapControl()
        {
            this.refreshBitmapDelegate = new RefreshBitmapDelegate(RefreshBitmap);
            this.BackColor = Color.Green;
            this.MouseDown += new MouseEventHandler(MapControl_MouseDown);
            this.MouseMove += new MouseEventHandler(MapControl_MouseMove);
            this.MouseUp += new MouseEventHandler(MapControl_MouseUp);
            this.Resize += new EventHandler(MapControl_OnResize);
            session.Zoom = 1;
            session.RefreshBitmap = renderer.LoadBitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Map.Resources.Refresh.png"));
            session.FitPOIToDimensions(this.ClientSize.Width, this.ClientSize.Height, 3, new GeoPoint(47.0, 19.0));
        }

        public void Highlight()
        {

            //this.Image = AdjustBrightness(bitmap, 110);
        }

        public long AddMarker(double lat, double lon, MarkerDescriptor dsc)
        {
            lock (sync)
            {
                // create the marker
                MapOverlay overlay = new MapOverlay(new GeoPoint(lat, lon), dsc.Offset);

                // add background image
                if (dsc.BgImageStream != null)
                {
                    IMapDrawable markerBg = renderer.LoadBitmap(dsc.BgImageStream);
                    overlay.AddElement(markerBg);
                }

                // add image
                if (dsc.Bitmap != null)
                {
                    StandardBitmap bitmap = new StandardBitmap(dsc.Bitmap);
                    bitmap.Offset = dsc.ImageOffset;
                    overlay.AddElement(bitmap);
                }

                // add text
                if (dsc.Text != null && dsc.Text.Length > 0)
                {
                    TextMapDrawable text = new TextMapDrawable();
                    text.Text = dsc.Text;
                    if (dsc.Font != null)
                        text.Font = dsc.Font;
                    else
                        text.Font = new Font(FontFamily.GenericSansSerif, 5.0f, FontStyle.Bold);
                    if (dsc.TextBrush != null)
                        text.Brush = dsc.TextBrush;
                    else
                        text.Brush = new SolidBrush(Color.Black);
                    overlay.AddElement(text);
                }

                session.Overlays.Add(overlay);

                this.Invoke(refreshBitmapDelegate);

                return overlay.Id;
            }
        }

        public bool MoveMarker(long id, double lat, double lon)
        {
            lock (sync)
            {
                foreach (MapOverlay m in session.Overlays)
                {
                    if (m.Id == id)
                    {
                        m.Point = new GeoPoint(lat, lon);

                        this.Invoke(refreshBitmapDelegate);

                        return true;
                    }
                }
            }
            return false;
        }

        public bool RemoveMarker(long id)
        {
            foreach (MapOverlay m in session.Overlays)
            {
                if (m.Id == id)
                {
                    session.Overlays.Remove(m);
                    RefreshBitmap();
                    return true;
                }
            }
            return false;
        }

        public void RefreshBitmap()
        {
            session.ClearAgedTiles(15000);

            if (bitmap == null || bitmap.Width != this.ClientSize.Width || bitmap.Height != this.ClientSize.Height)
            {
                bitmap = new Bitmap(this.ClientSize.Width, this.ClientSize.Height, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);
                renderer.Graphics = Graphics.FromImage(bitmap);

                this.Image = bitmap;
            }
            session.DrawMap(renderer, 0, 0, bitmap.Width, bitmap.Height, (o) =>
            {
                Invoke(new EventHandler((sender, args) =>
                {
                    RefreshBitmap();
                }));
            }, null);
            this.Refresh();
        }

        public void ZoomIn()
        {
            session.ZoomIn();
            RefreshBitmap();
        }

        public void ZoomOut()
        {
            session.ZoomOut();
            RefreshBitmap();
        }

        private void MapControl_MouseDown(object sender, MouseEventArgs e)
        {
            lastPos = MousePosition;
            if (State == MapState.AreaZoom)
            {
                bitmapBuffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);
                mapRatio = (double)(this.ClientSize.Width) / (double)(this.ClientSize.Height);
            }
        }

        void MapControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (State == MapState.AreaZoom)
            {
                GeoPoint[] bounds = new GeoPoint[2];
                bounds[0] = session.ScreenToGeoPoint(new Point(lastPos.X, lastPos.Y), ClientSize);
                bounds[1] = session.ScreenToGeoPoint(new Point(MousePosition.X, MousePosition.Y), ClientSize);

                session.FitPOIToDimensions(this.ClientSize.Width, this.ClientSize.Height, 15, bounds);
                State = MapState.Normal;
                this.Image = this.bitmap;
                RefreshBitmap();
            }
        }

        private void MapControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (State == MapState.Normal)
            {
                session.Pan(MousePosition.X - lastPos.X, MousePosition.Y - lastPos.Y);
                lastPos = MousePosition;
                RefreshBitmap();
            }
            else if (State == MapState.AreaZoom)
            {
                Graphics g = Graphics.FromImage(bitmapBuffer);
                g.DrawImage(bitmap, 0, 0);

                if(mapRatio < 1)
                    g.DrawRectangle(zoomRectanglePen, lastPos.X, lastPos.Y, MousePosition.X - lastPos.X, (int)((MousePosition.X - lastPos.X) / mapRatio));
                else
                    g.DrawRectangle(zoomRectanglePen, lastPos.X, lastPos.Y, (int)((MousePosition.Y - lastPos.Y) * mapRatio), MousePosition.Y - lastPos.Y);
                
                this.Image = this.bitmapBuffer;
            }
        }

        private void MapControl_OnResize(object sender, EventArgs e)
        {
            RefreshBitmap();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            session.Dispose();
            renderer.Dispose();
            bitmap.Dispose();
        }
    }
}
